package com.general.config;

import com.general.config.annotation.ConfigSource;
import com.general.config.annotation.ConfigSourceFactory;
import com.general.config.annotation.ConfigSources;
import com.general.config.annotation.DefaultConfigSourceFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: ConfigSourcesParser
 * @author: general
 * @version: 1.0
 * @create: 2021-10-27 20:25
 **/
public class ConfigSourcesParser {

    private static final ConfigSourceFactory DEFAULT_PROPERTY_SOURCE_FACTORY = new DefaultConfigSourceFactory();

    private final List<String> configSourceNames = new ArrayList<>();

    private ConfigSourceHolder configSourceHolder = new ConfigSourceHolder();

    private static final AnnotationMetadaParser annotationMetataParser = new AnnotationMetadaParser();

    public ConfigSourceHolder getConfigSourceHolder() {
        return configSourceHolder;
    }

    public void parseConfigSources(Class configClass){
        AnnotationMetadata annotationMetadata = annotationMetataParser.getAnnotationMetadataFromType(configClass);
        for (AnnotationAttributes propertySource : AnnotationUtilExt.attributesForRepeatable(annotationMetadata, ConfigSources.class, ConfigSource.class)) {
             processConfigSource(propertySource);
        }
    }

    private void processConfigSource(AnnotationAttributes configSource) {
        String name = configSource.getString("name");
        if (!StringUtils.hasLength(name)) {
            name = null;
        }
        String encoding = configSource.getString("encoding");
        if (!StringUtils.hasLength(encoding)) {
            encoding = null;
        }
        int ordinal = configSource.getNumber("ordinal");

        // configSourceFactory 实例化
        Class<? extends ConfigSourceFactory> factoryClass = configSource.getClass("factory");
        ConfigSourceFactory factory = (factoryClass == ConfigSourceFactory.class ?
                DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

        String resource = configSource.getString("resource");
        Assert.isTrue(resource.length() > 0, "At least one @ConfigSource(value) location is required");
        try {
            URL resourceURL = new URL(resource);
            addConfigSource(factory.createConfigSource(name, ordinal, resourceURL, encoding));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void addConfigSource(org.eclipse.microprofile.config.spi.ConfigSource configSource) {
        String name = configSource.getName();
        MutableConfigSources propertySources = configSourceHolder.getConfigSources();
        if (this.configSourceNames.contains(name)) {
            // We've already added a version, we need to extend it
            org.eclipse.microprofile.config.spi.ConfigSource existing = propertySources.get(name);
            if (existing != null) {
                int existingOrdinal = existing.getOrdinal();
                int toAddOrdinal = configSource.getOrdinal();
                // 根据优先级 优先级大的放在前面
                if(toAddOrdinal > existingOrdinal){
                    propertySources.addFirst(configSource);
                }
                else{
                    propertySources.addLast(configSource);
                }
                return;
            }
        }

        if (this.configSourceNames.isEmpty()) {
            propertySources.addLast(configSource);
        }
        else {
            String firstProcessed = this.configSourceNames.get(this.configSourceNames.size() - 1);
            propertySources.addBefore(firstProcessed, configSource);
        }
        this.configSourceNames.add(name);
    }
}


